﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Schema;

namespace GrainManage.Server.Tool
{
    public sealed class ApiUtil
    {
        private static readonly List<string> exceptedClassList = new List<string> { "List`1", "Dictionary`2" };
        public static readonly List<string> ControllerList = new List<string> { "Account", "Column", "Contact", "District", "Image", "Price", "Status", "Trade", "Weather", "Encrypt" };

        private static readonly string controllerPattern = null;
        private static readonly string controllerCatalogPattern = null;
        private static readonly string methodPattern = null;
        private static readonly string urlPattern = null;
        private static readonly string titlePattern = null;
        private static readonly string paramePattern = null;
        private static readonly string returnedPattern = null;
        private static readonly string contentPattern = null;

        static ApiUtil()
        {
            var mode = GrainManage.Server.Factory.ObjectFactory.GetAppConfig("UsePatternInDB");
            if (!string.IsNullOrEmpty(mode) && mode.ToLower() == "true")
            {
                controllerPattern = GetPattern("controllerPattern");
                controllerCatalogPattern = GetPattern("controllerCatalogPattern");
                methodPattern = GetPattern("methodPattern");
                urlPattern = GetPattern("urlPattern");
                titlePattern = GetPattern("titlePattern");
                paramePattern = GetPattern("paramePattern");
                returnedPattern = GetPattern("returnedPattern");
                contentPattern = GetPattern("contentPattern");
            }
            else
            {
                controllerPattern = "<h1><font color=blue>#ControllerName#</font></h1>";
                controllerCatalogPattern = "<td><h2><a href='#Domain#/#ControllerName#/' style='text-decoration: none;font-color:blue'>#ControllerName#&nbsp;&nbsp;&nbsp;&nbsp;</a></h2></td>";
                methodPattern = "<h2 id=#AnchorName#><font color=red>#MethodCount#. #MethodName#</font></h2>";
                urlPattern = "<div><font color=blue>#Domain#/#ControllerName#/#MethodName#?#Parameters#</font></div>";
                titlePattern = "<h3><font color=darkviolet>#IOType#: </font></h3>";
                paramePattern = "<div><font color=gray>#ParameterCount#. </font><font color=forestgreen>#ParameterName# : #ParameterTypeName#</font></div>";
                returnedPattern = "<h3><font color=darkviolet>#IOType#: </font></h3><div><font color=forestgreen>#ParameterTypeName#</font></div>";
                contentPattern = "<pre>#Content#</pre>";
            }
            var domain = GetDomain();
            controllerCatalogPattern = controllerCatalogPattern.Replace("#Domain#", domain);
            urlPattern = urlPattern.Replace("#Domain#", domain);
        }

        public static string GetApiInfo<TController, TBusiness>(bool isSchema)
        {
            int methodCount = 0;
            //var controllerType = typeof(TController);
            var businessType = typeof(TBusiness);
            var controllerName = businessType.Name.Replace("Business", string.Empty);
            var builder = new StringBuilder();
            AddControllerCatalog(builder, ControllerList);
            builder.Append(controllerPattern.Replace("#ControllerName#", controllerName));
            var methods = businessType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
            AddMethodCatalog(builder, methods);
            JsonSchemaGenerator schemaGenerator = isSchema ? new JsonSchemaGenerator() : null;
            foreach (var method in methods)
            {
                var inputParames = method.GetParameters();
                var count = (++methodCount).ToString();
                builder.Append(methodPattern.Replace("#AnchorName#", count).Replace("#MethodCount#", count).Replace("#MethodName#", method.Name));
                var mainUrl = urlPattern.Replace("#ControllerName#", controllerName).Replace("#MethodName#", method.Name);
                AddUrl(builder, mainUrl, inputParames);
                AddInput(builder, inputParames, isSchema, schemaGenerator);
                AddOutput(builder, method.ReturnType, isSchema, schemaGenerator);
            }
            return builder.ToString();
        }

        #region 辅助方法

        private static void AddUrl(StringBuilder builder, string mainUrl, ParameterInfo[] inputParames)
        {

            if (inputParames.Length > 0 && inputParames[0].Name != "input")
            {
                string paraPattern = string.Empty;
                foreach (var para in inputParames)
                {
                    //paraPattern += (para.Name + "=" + "$" + para.Name + "$&");
                    paraPattern += (para.Name + "=" + para.DefaultValue + "&");
                }
                paraPattern = paraPattern.Remove(paraPattern.Length - 1, 1);
                builder.Append(mainUrl.Replace("#Parameters#", paraPattern));
            }
            else
            {
                builder.Append(mainUrl.Replace("?#Parameters#", string.Empty));
            }
        }

        private static void AddControllerCatalog(StringBuilder builder, List<string> controllers)
        {
            int number = 12;
            builder.Append("<table align='center'>");
            //builder.Append(style);
            for (int i = 0; i < controllers.Count; i++)
            {
                int mod = i % number;
                if (mod == 0)
                {
                    builder.Append("<tr>");
                    builder.Append(controllerCatalogPattern.Replace("#ControllerName#", controllers[i]));
                }
                else if (mod == number - 1)
                {
                    builder.Append(controllerCatalogPattern.Replace("#ControllerName#", controllers[i]));
                    builder.Append("</tr>");
                }
                else
                {
                    builder.Append(controllerCatalogPattern.Replace("#ControllerName#", controllers[i]));
                }
            }
            if (!builder.ToString().EndsWith("</tr>"))
            {
                builder.Append("</tr>");
            }
            builder.Append("</table>");
        }

        private static void AddMethodCatalog(StringBuilder builder, MethodInfo[] methods)
        {

            int count = 0;
            builder.Append("<ul>");
            foreach (var method in methods)
            {
                //if (!exceptedMethodList.Contains(method.Name))
                //{
                ++count;
                builder.Append(string.Format("<li><a href=#{0}>{1}. {2}</a></li>", count, count, method.Name));
                //}
            }

            builder.Append("</ul>");

        }

        private static void AddInput(StringBuilder builder, ParameterInfo[] inputParames, bool isSchema, JsonSchemaGenerator schemaGenerator)
        {
            builder.Append(titlePattern.Replace("#IOType#", "Input Type"));
            int paraCount = 0;
            foreach (var para in inputParames)
            {
                var paraType = para.ParameterType;
                builder.Append(paramePattern.Replace("#ParameterCount#", (++paraCount).ToString()).Replace("#ParameterName#", para.Name).Replace("#ParameterTypeName#", paraType.Name));
                if (isSchema)
                {
                    AddSchema(builder, paraType, schemaGenerator);
                }
                else
                {
                    AddContent(builder, paraType);
                }
            }
        }

        private static void AddOutput(StringBuilder builder, Type returnedType, bool isSchema, JsonSchemaGenerator schemaGenerator)
        {
            builder.Append(returnedPattern.Replace("#IOType#", "Output Type").Replace("#ParameterTypeName#", returnedType.Name));
            if (isSchema)
            {
                AddSchema(builder, returnedType, schemaGenerator);
            }
            else
            {
                AddContent(builder, returnedType);
            }

        }

        private static void AddContent(StringBuilder builder, Type paraType)
        {
            if (!paraType.Namespace.StartsWith("System") || exceptedClassList.Contains(paraType.Name))
            {
                var content = Activator.CreateInstance(paraType);
                if (content != null)
                {
                    CreateContent(content, paraType);
                    builder.Append(contentPattern.Replace("#Content#", JsonConvert.SerializeObject(content, Formatting.Indented)));
                }
            }
        }

        private static void CreateContent<TContent>(TContent contentInstance, Type contentType) where TContent : class
        {
            if (!contentType.Namespace.StartsWith("System"))
            {
                var contentProperties = contentType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                foreach (var contentProperty in contentProperties)
                {
                    var contentPropertyType = contentProperty.PropertyType;
                    if (!contentPropertyType.Namespace.StartsWith("System") || exceptedClassList.Contains(contentPropertyType.Name))
                    {
                        if (!contentPropertyType.Namespace.StartsWith("System") && contentPropertyType == contentType)   //防止死循环
                        {
                            break;
                        }
                        var contentPropertyInstance = Activator.CreateInstance(contentPropertyType);
                        contentProperty.SetValue(contentInstance, contentPropertyInstance, null);
                        CreateContent(contentPropertyInstance, contentPropertyType);
                    }
                    else if (contentPropertyType == typeof(string))
                    {
                        contentProperty.SetValue(contentInstance, string.Empty, null);
                    }
                }
            }
            else if (contentType.Name == "List`1")
            {
                var contentArgumentTypes = contentType.GetGenericArguments();
                if (contentArgumentTypes != null)
                {
                    foreach (var contentArgumentType in contentArgumentTypes)
                    {
                        if (!contentArgumentType.Namespace.StartsWith("System") || exceptedClassList.Contains(contentArgumentType.Name))
                        {
                            var contentArgumentInstance = Activator.CreateInstance(contentArgumentType);
                            var addMethod = contentType.GetMethod("Add");
                            addMethod.Invoke(contentInstance, new object[] { contentArgumentInstance });
                            CreateContent(contentArgumentInstance, contentArgumentType);
                        }
                        else if (contentArgumentType == typeof(string))
                        {
                            var addMethod = contentType.GetMethod("Add");
                            addMethod.Invoke(contentInstance, new object[] { "item1" });
                            addMethod.Invoke(contentInstance, new object[] { "item2" });
                        }
                    }
                }
            }
            else if (contentType.Name == "Dictionary`2")
            {
                var contentArgumentTypes = contentType.GetGenericArguments();
                if (contentArgumentTypes != null && contentArgumentTypes.Length == 2)
                {
                    var contentArgumentKeyType = contentArgumentTypes[0];
                    var contentArgumentValueType = contentArgumentTypes[1];
                    if (contentArgumentKeyType == typeof(string))
                    {
                        if (!contentArgumentValueType.Namespace.StartsWith("System") || exceptedClassList.Contains(contentArgumentValueType.Name))
                        {
                            var addMethod = contentType.GetMethod("Add");
                            var contentArgumentValueInstance = Activator.CreateInstance(contentArgumentValueType);
                            addMethod.Invoke(contentInstance, new object[] { "key", contentArgumentValueInstance });
                            CreateContent(contentArgumentValueInstance, contentArgumentValueType);
                        }
                        else if (contentArgumentValueType == typeof(string))
                        {
                            var addMethod = contentType.GetMethod("Add");
                            addMethod.Invoke(contentInstance, new object[] { "key1", "value1" });
                            addMethod.Invoke(contentInstance, new object[] { "key2", "value2" });
                        }
                    }
                    else if (contentArgumentKeyType == typeof(int) && contentArgumentValueType == typeof(string))
                    {
                        var addMethod = contentType.GetMethod("Add");
                        addMethod.Invoke(contentInstance, new object[] { 1, "value1" });
                        addMethod.Invoke(contentInstance, new object[] { 2, "value2" });
                    }
                }
            }
        }

        private static void AddSchema(StringBuilder builder, Type paraType, JsonSchemaGenerator schemaGenerator)
        {
            if (!paraType.Namespace.StartsWith("System") || exceptedClassList.Contains(paraType.Name))
            {
                var schema = schemaGenerator.Generate(paraType);
                var schemaContent = schema.ToString();
                schemaContent = System.Text.RegularExpressions.Regex.Replace(schemaContent, "\r\n *\"required\": true,", string.Empty);
                schemaContent = System.Text.RegularExpressions.Regex.Replace(schemaContent, ",\r\n *\"null\"", string.Empty);
                builder.Append(contentPattern.Replace("#Content#", schemaContent));
            }
        }

        private static string GetPattern(string key)
        {
            return GrainManage.Server.Factory.ObjectFactory.PatternService.GetContent(key);
        }

        private static string GetDomain()
        {
            var domain = HttpContext.Current.Request.Url.AbsoluteUri.TrimEnd('/');
            if (domain.EndsWith("Index", StringComparison.CurrentCultureIgnoreCase))
            {
                //多删一个字符,是因为要去掉 "/"符号
                domain = domain.Substring(0, domain.Length - 6);
            }
            else if (domain.EndsWith("Schema", StringComparison.CurrentCultureIgnoreCase))
            {
                domain = domain.Substring(0, domain.Length - 7);
            }

            foreach (var controllerName in ControllerList)
            {
                if (domain.EndsWith(controllerName, StringComparison.CurrentCultureIgnoreCase))
                {
                    domain = domain.Substring(0, domain.Length - controllerName.Length - 1);
                    break;
                }
            }
            return domain;
        }

        #endregion
    }
}